<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <title>操作系统 - Even - A super concise theme for Hugo</title>
  <meta name="renderer" content="webkit" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>

<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />

<meta name="theme-color" content="#f8f5ec" />
<meta name="msapplication-navbutton-color" content="#f8f5ec">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="#f8f5ec">


<meta name="author" content="olOwOlo" /><meta name="description" content="概述 基本特征 并发 并发是指宏观上在一段时间内能同时运行多个程序，而并行则指同一时刻能运行多个指令。 并行需要硬件支持，如多流水线、多核处理器或者" /><meta name="keywords" content="Hugo, theme, even" />






<meta name="generator" content="Hugo 0.83.1 with theme even" />


<link rel="canonical" href="https://xiongshou.github.io/post/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/manifest.json">
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">



<link href="/sass/main.min.f92fd13721ddf72129410fd8250e73152cc6f2438082b6c0208dc24ee7c13fc4.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.1.20/dist/jquery.fancybox.min.css" integrity="sha256-7TyXnr2YU040zfSP+rEcz29ggW4j56/ujTPwjMzyqFY=" crossorigin="anonymous">


<meta property="og:title" content="操作系统" />
<meta property="og:description" content="概述 基本特征 并发 并发是指宏观上在一段时间内能同时运行多个程序，而并行则指同一时刻能运行多个指令。 并行需要硬件支持，如多流水线、多核处理器或者" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://xiongshou.github.io/post/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/" /><meta property="article:section" content="post" />
<meta property="article:published_time" content="2021-06-01T20:16:50&#43;12:00" />
<meta property="article:modified_time" content="2021-06-01T20:16:50&#43;12:00" />

<meta itemprop="name" content="操作系统">
<meta itemprop="description" content="概述 基本特征 并发 并发是指宏观上在一段时间内能同时运行多个程序，而并行则指同一时刻能运行多个指令。 并行需要硬件支持，如多流水线、多核处理器或者"><meta itemprop="datePublished" content="2021-06-01T20:16:50&#43;12:00" />
<meta itemprop="dateModified" content="2021-06-01T20:16:50&#43;12:00" />
<meta itemprop="wordCount" content="8694">
<meta itemprop="keywords" content="大学课程," /><meta name="twitter:card" content="summary"/>
<meta name="twitter:title" content="操作系统"/>
<meta name="twitter:description" content="概述 基本特征 并发 并发是指宏观上在一段时间内能同时运行多个程序，而并行则指同一时刻能运行多个指令。 并行需要硬件支持，如多流水线、多核处理器或者"/>

<!--[if lte IE 9]>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.1.20170427/classList.min.js"></script>
<![endif]-->

<!--[if lt IE 9]>
  <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
<![endif]-->

</head>
<body>
  <div id="mobile-navbar" class="mobile-navbar">
  <div class="mobile-header-logo">
    <a href="/" class="logo">Even</a>
  </div>
  <div class="mobile-navbar-icon">
    <span></span>
    <span></span>
    <span></span>
  </div>
</div>
<nav id="mobile-menu" class="mobile-menu slideout-menu">
  <ul class="mobile-menu-list">
    <a href="/">
        <li class="mobile-menu-item">Home</li>
      </a><a href="/post/">
        <li class="mobile-menu-item">Archives</li>
      </a><a href="/tags/">
        <li class="mobile-menu-item">Tags</li>
      </a><a href="/categories/">
        <li class="mobile-menu-item">Categories</li>
      </a><a href="/about/">
        <li class="mobile-menu-item">About</li>
      </a>
  </ul>

  


</nav>

  <div class="container" id="mobile-panel">
    <header id="header" class="header">
        <div class="logo-wrapper">
  <a href="/" class="logo">Even</a>
</div>





<nav class="site-navbar">
  <ul id="menu" class="menu">
    <li class="menu-item">
        <a class="menu-item-link" href="/">Home</a>
      </li><li class="menu-item">
        <a class="menu-item-link" href="/post/">Archives</a>
      </li><li class="menu-item">
        <a class="menu-item-link" href="/tags/">Tags</a>
      </li><li class="menu-item">
        <a class="menu-item-link" href="/categories/">Categories</a>
      </li><li class="menu-item">
        <a class="menu-item-link" href="/about/">About</a>
      </li>
  </ul>
</nav>

    </header>

    <main id="main" class="main">
      <div class="content-wrapper">
        <div id="content" class="content">
          <article class="post">
    
    <header class="post-header">
      <h1 class="post-title">操作系统</h1>

      <div class="post-meta">
        <span class="post-time"> 2021-06-01 </span>
        <div class="post-category">
            <a href="/categories/%E5%A4%A7%E5%AD%A6%E8%AF%BE%E7%A8%8B/"> 大学课程 </a>
            </div>
        
      </div>
    </header>

    <div class="post-toc" id="post-toc">
  <h2 class="post-toc-title">Contents</h2>
  <div class="post-toc-content always-active">
    <nav id="TableOfContents">
  <ul>
    <li><a href="#概述">概述</a>
      <ul>
        <li><a href="#基本特征">基本特征</a></li>
        <li><a href="#基本功能">基本功能</a></li>
        <li><a href="#系统调用">系统调用</a></li>
        <li><a href="#宏内核和微内核">宏内核和微内核</a>
          <ul>
            <li><a href="#1-宏内核">1. 宏内核</a></li>
            <li><a href="#2-微内核">2. 微内核</a></li>
          </ul>
        </li>
        <li><a href="#中断分类">中断分类</a>
          <ul>
            <li><a href="#1-外中断">1. 外中断</a></li>
            <li><a href="#2-异常">2. 异常</a></li>
            <li><a href="#3-陷入">3. 陷入</a></li>
          </ul>
        </li>
      </ul>
    </li>
    <li><a href="#进程管理">进程管理</a>
      <ul>
        <li><a href="#进程与线程">进程与线程</a></li>
        <li><a href="#区别">区别</a></li>
        <li><a href="#进程状态的切换">进程状态的切换</a></li>
        <li><a href="#进程调度算法">进程调度算法</a>
          <ul>
            <li><a href="#批处理系统">批处理系统</a></li>
            <li><a href="#交互式系统">交互式系统</a></li>
            <li><a href="#3-实时系统">3. 实时系统</a></li>
          </ul>
        </li>
        <li><a href="#进程同步">进程同步</a>
          <ul>
            <li><a href="#1-临界区">1. 临界区</a></li>
            <li><a href="#2-同步与互斥">2. 同步与互斥</a></li>
            <li><a href="#3-信号量">3. 信号量</a></li>
            <li><a href="#生产者消费者">生产者&amp;消费者</a></li>
            <li><a href="#4-管程">4. 管程</a></li>
          </ul>
        </li>
        <li><a href="#经典同步问题">经典同步问题</a>
          <ul>
            <li><a href="#1-哲学家进餐问题">1. 哲学家进餐问题</a></li>
            <li><a href="#2-读者-写者问题">2. 读者-写者问题</a></li>
          </ul>
        </li>
        <li><a href="#进程通信">进程通信</a>
          <ul>
            <li><a href="#1-管道">1. 管道</a></li>
            <li><a href="#2-fifo">2. FIFO</a></li>
            <li><a href="#3-消息队列">3. 消息队列</a></li>
            <li><a href="#4-信号量">4. 信号量</a></li>
            <li><a href="#5-共享存储">5. 共享存储</a></li>
            <li><a href="#6-套接字">6. 套接字</a></li>
          </ul>
        </li>
      </ul>
    </li>
    <li><a href="#死锁">死锁</a>
      <ul>
        <li><a href="#必要条件">必要条件</a></li>
        <li><a href="#处理方法">处理方法</a></li>
        <li><a href="#鸵鸟策略">鸵鸟策略</a></li>
        <li><a href="#死锁检测与死锁恢复">死锁检测与死锁恢复</a>
          <ul>
            <li><a href="#1-每种类型一个资源的死锁检测">1. 每种类型一个资源的死锁检测</a></li>
            <li><a href="#2-每种类型多个资源的死锁检测">2. 每种类型多个资源的死锁检测</a></li>
            <li><a href="#3-死锁恢复">3. 死锁恢复</a></li>
          </ul>
        </li>
        <li><a href="#死锁预防">死锁预防</a>
          <ul>
            <li><a href="#1-破坏互斥条件">1. 破坏互斥条件</a></li>
            <li><a href="#2-破坏占有和等待条件">2. 破坏占有和等待条件</a></li>
            <li><a href="#3-破坏不可抢占条件">3. 破坏不可抢占条件</a></li>
            <li><a href="#4-破坏环路等待">4. 破坏环路等待</a></li>
          </ul>
        </li>
        <li><a href="#死锁避免">死锁避免</a>
          <ul>
            <li><a href="#1-安全状态">1. 安全状态</a></li>
            <li><a href="#2-单个资源的银行家算法">2. 单个资源的银行家算法</a></li>
            <li><a href="#3-多个资源的银行家算法">3. 多个资源的银行家算法</a></li>
          </ul>
        </li>
      </ul>
    </li>
    <li><a href="#内存管理">内存管理</a>
      <ul>
        <li><a href="#虚拟内存">虚拟内存</a></li>
        <li><a href="#分页系统地址映射">分页系统地址映射</a></li>
        <li><a href="#页面置换算法">页面置换算法</a>
          <ul>
            <li><a href="#1-最佳">1. 最佳</a></li>
            <li><a href="#2-最近最久未使用">2. 最近最久未使用</a></li>
            <li><a href="#4-先进先出">4. 先进先出</a></li>
            <li><a href="#5-第二次机会算法">5. 第二次机会算法</a></li>
            <li><a href="#6-时钟">6. 时钟</a></li>
          </ul>
        </li>
        <li><a href="#分段">分段</a></li>
        <li><a href="#段页式">段页式</a></li>
        <li><a href="#分页与分段的比较">分页与分段的比较</a></li>
      </ul>
    </li>
    <li><a href="#链接">链接</a>
      <ul>
        <li><a href="#编译系统">编译系统</a></li>
        <li><a href="#静态链接">静态链接</a></li>
        <li><a href="#目标文件">目标文件</a></li>
        <li><a href="#动态链接">动态链接</a></li>
      </ul>
    </li>
  </ul>
</nav>
  </div>
</div>
    <div class="post-content">
      <h1 id="概述">概述</h1>
<h2 id="基本特征">基本特征</h2>
<ol>
<li>并发</li>
</ol>
<p>并发是指宏观上在一段时间内能同时运行多个程序，而并行则指同一时刻能运行多个指令。</p>
<p>并行需要硬件支持，如多流水线、多核处理器或者分布式计算系统。</p>
<p>操作系统通过引入进程和线程，使得程序能够并发运行。</p>
<ol start="2">
<li>共享</li>
</ol>
<p>共享是指系统中的资源可以被多个并发进程共同使用。</p>
<p>有两种共享方式：互斥共享和同时共享。</p>
<p>互斥共享的资源称为临界资源，例如打印机等，在同一时刻只允许一个进程访问，需要用同步机制来实现互斥访问。</p>
<ol start="3">
<li>虚拟</li>
</ol>
<p>虚拟技术把一个物理实体转换为多个逻辑实体。</p>
<p>主要有两种虚拟技术：时（时间）分复用技术和空（空间）分复用技术。</p>
<p>多个进程能在同一个处理器上并发执行使用了时分复用技术，让每个进程轮流占用处理器，每次只执行一小个时间片并快速切换。</p>
<p>虚拟内存使用了空分复用技术，它将物理内存抽象为地址空间，每个进程都有各自的地址空间。地址空间的页被映射到物理内存，地址空间的页并不需要全部在物理内存中，当使用到一个没有在物理内存的页时，执行页面置换算法，将该页置换到内存中。</p>
<ol start="4">
<li>异步</li>
</ol>
<p>异步指进程不是一次性执行完毕，而是走走停停，以不可知的速度向前推进。</p>
<h2 id="基本功能">基本功能</h2>
<ol>
<li>进程管理</li>
</ol>
<p>进程控制、进程同步、进程通信、死锁处理、处理机调度等。</p>
<ol start="2">
<li>内存管理</li>
</ol>
<p>内存分配、地址映射、内存保护与共享、虚拟内存等。</p>
<ol start="3">
<li>文件管理</li>
</ol>
<p>文件存储空间的管理、目录管理、文件读写管理和保护等。</p>
<ol start="4">
<li>设备管理</li>
</ol>
<p>完成用户的 I/O 请求，方便用户使用各种设备，并提高设备的利用率。</p>
<p>主要包括缓冲管理、设备分配、设备处理、虛拟设备等。</p>
<h2 id="系统调用">系统调用</h2>
<p>如果一个进程在用户态需要使用内核态的功能，就进行系统调用从而陷入内核，由操作系统代为完成。</p>
<p><a href="https://camo.githubusercontent.com/e6e9338fcb2f8c849b5ed9798862d27937d80c94721948dd87c5dec1e739c2c6/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f74475056302e706e67"><img src="https://camo.githubusercontent.com/e6e9338fcb2f8c849b5ed9798862d27937d80c94721948dd87c5dec1e739c2c6/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f74475056302e706e67" alt="img"></a></p>
<p>Linux 的系统调用主要有以下这些：</p>
<table>
<thead>
<tr>
<th>Task</th>
<th>Commands</th>
</tr>
</thead>
<tbody>
<tr>
<td>进程控制</td>
<td>fork(); exit(); wait();</td>
</tr>
<tr>
<td>进程通信</td>
<td>pipe(); shmget(); mmap();</td>
</tr>
<tr>
<td>文件操作</td>
<td>open(); read(); write();</td>
</tr>
<tr>
<td>设备操作</td>
<td>ioctl(); read(); write();</td>
</tr>
<tr>
<td>信息维护</td>
<td>getpid(); alarm(); sleep();</td>
</tr>
<tr>
<td>安全</td>
<td>chmod(); umask(); chown();</td>
</tr>
</tbody>
</table>
<h2 id="宏内核和微内核">宏内核和微内核</h2>
<h3 id="1-宏内核">1. 宏内核</h3>
<p>宏内核是将操作系统功能作为一个紧密结合的整体放到内核。</p>
<p>由于各模块共享信息，因此有很高的性能。</p>
<h3 id="2-微内核">2. 微内核</h3>
<p>由于操作系统不断复杂，因此将一部分操作系统功能移出内核，从而降低内核的复杂性。移出的部分根据分层的原则划分成若干服务，相互独立。</p>
<p>在微内核结构下，操作系统被划分成小的、定义良好的模块，只有微内核这一个模块运行在内核态，其余模块运行在用户态。</p>
<p>因为需要频繁地在用户态和核心态之间进行切换，所以会有一定的性能损失。</p>
<p><a href="https://camo.githubusercontent.com/e244b7965823da98c230d7b71038b8ee11dcb2e30b5e8fb1272dcd76008a889f/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f325f31345f6d6963726f6b65726e656c4172636869746563747572652e6a7067"><img src="https://camo.githubusercontent.com/e244b7965823da98c230d7b71038b8ee11dcb2e30b5e8fb1272dcd76008a889f/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f325f31345f6d6963726f6b65726e656c4172636869746563747572652e6a7067" alt="img"></a></p>
<h2 id="中断分类">中断分类</h2>
<h3 id="1-外中断">1. 外中断</h3>
<p>由 CPU 执行指令以外的事件引起，如 I/O 完成中断，表示设备输入/输出处理已经完成，处理器能够发送下一个输入/输出请求。此外还有时钟中断、控制台中断等。</p>
<h3 id="2-异常">2. 异常</h3>
<p>由 CPU 执行指令的内部事件引起，如非法操作码、地址越界、算术溢出等。</p>
<h3 id="3-陷入">3. 陷入</h3>
<p>在用户程序中使用系统调用。</p>
<h1 id="进程管理">进程管理</h1>
<h2 id="进程与线程">进程与线程</h2>
<p>进程是资源分配的基本单位。</p>
<p>进程控制块 ( PCB) 描述进程的基本信息和运行状态，所谓的创建进程和撤销进程，都是指对 PCB 的操作。</p>
<p>线程是独立调度的基本单位。</p>
<p>一个进程中可以有多个线程，它们共享进程资源。</p>
<h2 id="区别">区别</h2>
<ul>
<li>拥有资源：进程是资源分配的基本单位，但是线程不拥有资源，线程可以访问隶属进程的资源。</li>
<li>调度：线程是独立调度的基本单位，在同一进程中，线程的切换不会引起进程切换，从一个进程中的线程切换到另一个进程中的线程时，会引起进程切换。</li>
<li>系统开销：由于创建或撤销进程时，系统都要为之分配或回收资源，如内存空间、I/O 设备等，所付出的开销远大于创建或撤销线程时的开销。类似地，在进行进程切换时，涉及当前执行进程 CPU 环境的保存及新调度进程 CPU 环境的设置，而线程切换时只需保存和设置少量寄存器内容，开销很小。</li>
<li>通信方面：线程间可以通过直接读写同一进程中的数据进行通信，但是进程通信需要借助 IPC。</li>
</ul>
<h2 id="进程状态的切换">进程状态的切换</h2>
<ul>
<li>就绪状态（ready）：等待被调度</li>
<li>运行状态（running）</li>
<li>阻塞状态（waiting）：等待资源</li>
</ul>
<p>应该注意以下内容：</p>
<ul>
<li>只有就绪态和运行态可以相互转换，其它的都是单向转换。就绪状态的进程通过调度算法从而获得 CPU 时间，转为运行状态；而运行状态的进程，在分配给它的 CPU 时间片用完之后就会转为就绪状态，等待下一次调度。</li>
<li>阻塞状态是缺少需要的资源从而由运行状态转换而来，但是该资源不包括 CPU 时间，缺少 CPU 时间会从运行态转换为就绪态。</li>
</ul>
<h2 id="进程调度算法">进程调度算法</h2>
<p>不同环境的调度算法目标不同，因此需要针对不同环境来讨论调度算法。</p>
<h3 id="批处理系统">批处理系统</h3>
<p>批处理系统没有太多的用户操作，在该系统中，调度算法目标是保证吞吐量和周转时间（从提交到终止的时间）。</p>
<p><strong>1.1 先来先服务 first-come first-serverd（FCFS）</strong></p>
<p>非抢占式的调度算法，按照请求的顺序进行调度。</p>
<p>有利于长作业，但不利于短作业，因为短作业必须一直等待前面的长作业执行完毕才能执行，而长作业又需要执行很长时间，造成了短作业等待时间过长。</p>
<p><strong>1.2 短作业优先 shortest job first（SJF）</strong></p>
<p>非抢占式的调度算法，按估计运行时间最短的顺序进行调度。</p>
<p>长作业有可能会饿死，处于一直等待短作业执行完毕的状态。因为如果一直有短作业到来，那么长作业永远得不到调度。</p>
<p><strong>1.3 最短剩余时间优先 shortest remaining time next（SRTN）</strong></p>
<p>最短作业优先的抢占式版本，按剩余运行时间的顺序进行调度。 当一个新的作业到达时，其整个运行时间与当前进程的剩余时间作比较。如果新的进程需要的时间更少，则挂起当前进程，运行新的进程。否则新的进程等待。</p>
<h3 id="交互式系统">交互式系统</h3>
<p>交互式系统有大量的用户交互操作，在该系统中调度算法的目标是快速地进行响应。</p>
<p><strong>2.1 时间片轮转</strong></p>
<p>将所有就绪进程按 FCFS 的原则排成一个队列，每次调度时，把 CPU 时间分配给队首进程，该进程可以执行一个时间片。当时间片用完时，由计时器发出时钟中断，调度程序便停止该进程的执行，并将它送往就绪队列的末尾，同时继续把 CPU 时间分配给队首的进程。</p>
<p>时间片轮转算法的效率和时间片的大小有很大关系：</p>
<ul>
<li>因为进程切换都要保存进程的信息并且载入新进程的信息，如果时间片太小，会导致进程切换得太频繁，在进程切换上就会花过多时间。</li>
<li>而如果时间片过长，那么实时性就不能得到保证。</li>
</ul>
<p><a href="https://camo.githubusercontent.com/a87daa8201015ff54a213d9ea95c1e49e7eec447938c441dd0247e80b18eaa05/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f38633636323939392d633136632d343831632d396634302d3166646261356263393136372e706e67"><img src="https://camo.githubusercontent.com/a87daa8201015ff54a213d9ea95c1e49e7eec447938c441dd0247e80b18eaa05/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f38633636323939392d633136632d343831632d396634302d3166646261356263393136372e706e67" alt="img"></a></p>
<p><strong>2.2 优先级调度</strong></p>
<p>为每个进程分配一个优先级，按优先级进行调度。</p>
<p>为了防止低优先级的进程永远等不到调度，可以随着时间的推移增加等待进程的优先级。</p>
<p><strong>2.3 多级反馈队列</strong></p>
<p>一个进程需要执行 100 个时间片，如果采用时间片轮转调度算法，那么需要交换 100 次。</p>
<p>多级队列是为这种需要连续执行多个时间片的进程考虑，它设置了多个队列，每个队列时间片大小都不同，例如 1,2,4,8,..。进程在第一个队列没执行完，就会被移到下一个队列。这种方式下，之前的进程只需要交换 7 次。</p>
<p>每个队列优先权也不同，最上面的优先权最高。因此只有上一个队列没有进程在排队，才能调度当前队列上的进程。</p>
<p>可以将这种调度算法看成是时间片轮转调度算法和优先级调度算法的结合。</p>
<p><a href="https://camo.githubusercontent.com/c20fd7a3268ebc4ef0bce390344de2c5358392ecef2413d849c3095e21047980/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f30343263663932382d336338652d343831352d616539632d6632373830323032633638662e706e67"><img src="https://camo.githubusercontent.com/c20fd7a3268ebc4ef0bce390344de2c5358392ecef2413d849c3095e21047980/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f30343263663932382d336338652d343831352d616539632d6632373830323032633638662e706e67" alt="img"></a></p>
<h3 id="3-实时系统">3. 实时系统</h3>
<p>实时系统要求一个请求在一个确定时间内得到响应。</p>
<p>分为硬实时和软实时，前者必须满足绝对的截止时间，后者可以容忍一定的超时。</p>
<h2 id="进程同步">进程同步</h2>
<h3 id="1-临界区">1. 临界区</h3>
<p>对临界资源进行访问的那段代码称为临界区。</p>
<p>为了互斥访问临界资源，每个进程在进入临界区之前，需要先进行检查。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-fallback" data-lang="fallback">// entry section
// critical section;
// exit section
</code></pre></td></tr></table>
</div>
</div><h3 id="2-同步与互斥">2. 同步与互斥</h3>
<ul>
<li>同步：多个进程因为合作产生的直接制约关系，使得进程有一定的先后执行关系。</li>
<li>互斥：多个进程在同一时刻只有一个进程能进入临界区。</li>
</ul>
<h3 id="3-信号量">3. 信号量</h3>
<p>信号量（Semaphore）是一个整型变量，可以对其执行 down 和 up 操作，也就是常见的 P 和 V 操作。</p>
<ul>
<li><strong>down</strong> : 如果信号量大于 0 ，执行 -1 操作；如果信号量等于 0，进程睡眠，等待信号量大于 0；</li>
<li><strong>up</strong> ：对信号量执行 +1 操作，唤醒睡眠的进程让其完成 down 操作。</li>
</ul>
<p>down 和 up 操作需要被设计成原语，不可分割，通常的做法是在执行这些操作的时候屏蔽中断。</p>
<p>如果信号量的取值只能为 0 或者 1，那么就成为了 <strong>互斥量（Mutex）</strong> ，0 表示临界区已经加锁，1 表示临界区解锁。</p>
<h3 id="生产者消费者">生产者&amp;消费者</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-fallback" data-lang="fallback">typedef int semaphore;
semaphore mutex = 1;
void P1() {
    down(&amp;mutex);
    // 临界区
    up(&amp;mutex);
}

void P2() {
    down(&amp;mutex);
    // 临界区
    up(&amp;mutex);
}
</code></pre></td></tr></table>
</div>
</div><p><font size=3> <strong>使用信号量实现生产者-消费者问题</strong> </font> </br></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-fallback" data-lang="fallback">#define N 100
typedef int semaphore;
semaphore mutex = 1;
semaphore empty = N;
semaphore full = 0;

void producer() {
    while(TRUE) {
        int item = produce_item();
        down(&amp;empty);
        down(&amp;mutex);
        insert_item(item);
        up(&amp;mutex);
        up(&amp;full);
    }
}

void consumer() {
    while(TRUE) {
        down(&amp;full);
        down(&amp;mutex);
        int item = remove_item();
        consume_item(item);
        up(&amp;mutex);
        up(&amp;empty);
    }
}
</code></pre></td></tr></table>
</div>
</div><h3 id="4-管程">4. 管程</h3>
<p>使用信号量机制实现的生产者消费者问题需要客户端代码做很多控制，而管程把控制的代码独立出来，不仅不容易出错，也使得客户端代码调用更容易。</p>
<p>c 语言不支持管程，下面的示例代码使用了类 Pascal 语言来描述管程。示例代码的管程提供了 insert() 和 remove() 方法，客户端代码通过调用这两个方法来解决生产者-消费者问题。</p>
<p>管程有一个重要特性：在一个时刻只能有一个进程使用管程。进程在无法继续执行的时候不能一直占用管程，否则其它进程永远不能使用管程。</p>
<p>管程引入了 <strong>条件变量</strong> 以及相关的操作：<strong>wait()</strong> 和 <strong>signal()</strong> 来实现同步操作。对条件变量执行 wait() 操作会导致调用进程阻塞，把管程让出来给另一个进程持有。signal() 操作用于唤醒被阻塞的进程。</p>
<p>*<em>使用管程实现生产者-消费者问题</em></p>
<h2 id="经典同步问题">经典同步问题</h2>
<p>生产者和消费者问题前面已经讨论过了。</p>
<h3 id="1-哲学家进餐问题">1. 哲学家进餐问题</h3>
<p><a href="https://camo.githubusercontent.com/7f8eb6362323b56a5dd8ec061d7ea0c5b0d07a842132598bbed860a8bb941317/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f61393037376630362d373538342d346632622d386332302d3361386534363932383832302e6a7067"><img src="https://camo.githubusercontent.com/7f8eb6362323b56a5dd8ec061d7ea0c5b0d07a842132598bbed860a8bb941317/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f61393037376630362d373538342d346632622d386332302d3361386534363932383832302e6a7067" alt="img"></a></p>
<p>五个哲学家围着一张圆桌，每个哲学家面前放着食物。哲学家的生活有两种交替活动：吃饭以及思考。当一个哲学家吃饭时，需要先拿起自己左右两边的两根筷子，并且一次只能拿起一根筷子。</p>
<p>下面是一种错误的解法，如果所有哲学家同时拿起左手边的筷子，那么所有哲学家都在等待其它哲学家吃完并释放自己手中的筷子，导致死锁。</p>
<p>为了防止死锁的发生，可以设置两个条件：</p>
<ul>
<li>必须同时拿起左右两根筷子；</li>
<li>只有在两个邻居都没有进餐的情况下才允许进餐。</li>
</ul>
<h3 id="2-读者-写者问题">2. 读者-写者问题</h3>
<p>允许多个进程同时对数据进行读操作，但是不允许读和写以及写和写操作同时发生。</p>
<p>一个整型变量 count 记录在对数据进行读操作的进程数量，一个互斥量 count_mutex 用于对 count 加锁，一个互斥量 data_mutex 用于对读写的数据加锁。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-fallback" data-lang="fallback">typedef int semaphore;
semaphore count_mutex = 1;
semaphore data_mutex = 1;
int count = 0;

void reader() {
    while(TRUE) {
        down(&amp;count_mutex);
        count++;
        if(count == 1) down(&amp;data_mutex); // 第一个读者需要对数据进行加锁，防止写进程访问
        up(&amp;count_mutex);
        read();
        down(&amp;count_mutex);
        count--;
        if(count == 0) up(&amp;data_mutex);
        up(&amp;count_mutex);
    }
}

void writer() {
    while(TRUE) {
        down(&amp;data_mutex);
        write();
        up(&amp;data_mutex);
    }
}
</code></pre></td></tr></table>
</div>
</div><h2 id="进程通信">进程通信</h2>
<p>进程同步与进程通信很容易混淆，它们的区别在于：</p>
<ul>
<li>进程同步：控制多个进程按一定顺序执行；</li>
<li>进程通信：进程间传输信息。</li>
</ul>
<p>进程通信是一种手段，而进程同步是一种目的。也可以说，为了能够达到进程同步的目的，需要让进程进行通信，传输一些进程同步所需要的信息。</p>
<h3 id="1-管道">1. 管道</h3>
<p>它具有以下限制：</p>
<ul>
<li>只支持半双工通信（单向交替传输）；</li>
<li>只能在父子进程或者兄弟进程中使用。</li>
</ul>
<p><a href="https://camo.githubusercontent.com/af6ac5de61c835b0fe14c799c244632fa04239ef2ca9421eee543392353297c8/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f35336364396164652d623061362d343339392d623464652d3766316662643036636466622e706e67"><img src="https://camo.githubusercontent.com/af6ac5de61c835b0fe14c799c244632fa04239ef2ca9421eee543392353297c8/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f35336364396164652d623061362d343339392d623464652d3766316662643036636466622e706e67" alt="img"></a></p>
<h3 id="2-fifo">2. FIFO</h3>
<p>也称为命名管道，去除了管道只能在父子进程中使用的限制。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-fallback" data-lang="fallback">#include &lt;sys/stat.h&gt;
int mkfifo(const char *path, mode_t mode);
int mkfifoat(int fd, const char *path, mode_t mode);
</code></pre></td></tr></table>
</div>
</div><p>FIFO 常用于客户-服务器应用程序中，FIFO 用作汇聚点，在客户进程和服务器进程之间传递数据。</p>
<p><a href="https://camo.githubusercontent.com/8c4dd36cf4d1509b9c3ae0500085617fee811f7a83602a71e64769753b66b66b/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f32616335306238312d643932612d343430312d623965632d6632313133656363333037362e706e67"><img src="https://camo.githubusercontent.com/8c4dd36cf4d1509b9c3ae0500085617fee811f7a83602a71e64769753b66b66b/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f32616335306238312d643932612d343430312d623965632d6632313133656363333037362e706e67" alt="img"></a></p>
<h3 id="3-消息队列">3. 消息队列</h3>
<p>相比于 FIFO，消息队列具有以下优点：</p>
<ul>
<li>消息队列可以独立于读写进程存在，从而避免了 FIFO 中同步管道的打开和关闭时可能产生的困难；</li>
<li>避免了 FIFO 的同步阻塞问题，不需要进程自己提供同步方法；</li>
<li>读进程可以根据消息类型有选择地接收消息，而不像 FIFO 那样只能默认地接收。</li>
</ul>
<h3 id="4-信号量">4. 信号量</h3>
<p>它是一个计数器，用于为多个进程提供对共享数据对象的访问。</p>
<h3 id="5-共享存储">5. 共享存储</h3>
<p>允许多个进程共享一个给定的存储区。因为数据不需要在进程之间复制，所以这是最快的一种 IPC。</p>
<p>需要使用信号量用来同步对共享存储的访问。</p>
<p>多个进程可以将同一个文件映射到它们的地址空间从而实现共享内存。另外 XSI 共享内存不是使用文件，而是使用内存的匿名段。</p>
<h3 id="6-套接字">6. 套接字</h3>
<p>与其它通信机制不同的是，它可用于不同机器间的进程通信。</p>
<h1 id="死锁">死锁</h1>
<h2 id="必要条件">必要条件</h2>
<ul>
<li>互斥：每个资源要么已经分配给了一个进程，要么就是可用的。</li>
<li>占有和等待：已经得到了某个资源的进程可以再请求新的资源。</li>
<li>不可抢占：已经分配给一个进程的资源不能强制性地被抢占，它只能被占有它的进程显式地释放。</li>
<li>环路等待：有两个或者两个以上的进程组成一条环路，该环路中的每个进程都在等待下一个进程所占有的资源。</li>
</ul>
<h2 id="处理方法">处理方法</h2>
<p>主要有以下四种方法：</p>
<ul>
<li>鸵鸟策略</li>
<li>死锁检测与死锁恢复</li>
<li>死锁预防</li>
<li>死锁避免</li>
</ul>
<h2 id="鸵鸟策略">鸵鸟策略</h2>
<p>把头埋在沙子里，假装根本没发生问题。</p>
<p>因为解决死锁问题的代价很高，因此鸵鸟策略这种不采取任务措施的方案会获得更高的性能。</p>
<p>当发生死锁时不会对用户造成多大影响，或发生死锁的概率很低，可以采用鸵鸟策略。</p>
<p>大多数操作系统，包括 Unix，Linux 和 Windows，处理死锁问题的办法仅仅是忽略它。</p>
<h2 id="死锁检测与死锁恢复">死锁检测与死锁恢复</h2>
<p>不试图阻止死锁，而是当检测到死锁发生时，采取措施进行恢复。</p>
<h3 id="1-每种类型一个资源的死锁检测">1. 每种类型一个资源的死锁检测</h3>
<p>上图为资源分配图，其中方框表示资源，圆圈表示进程。资源指向进程表示该资源已经分配给该进程，进程指向资源表示进程请求获取该资源。</p>
<p>图 a 可以抽取出环，如图 b，它满足了环路等待条件，因此会发生死锁。</p>
<p>每种类型一个资源的死锁检测算法是通过检测有向图是否存在环来实现，从一个节点出发进行深度优先搜索，对访问过的节点进行标记，如果访问了已经标记的节点，就表示有向图存在环，也就是检测到死锁的发生。</p>
<h3 id="2-每种类型多个资源的死锁检测">2. 每种类型多个资源的死锁检测</h3>
<p><a href="https://camo.githubusercontent.com/65aa9be0a5faea5fb50ec17101ce3e900688ad9079ac67fed8bb7fb40c8578f1/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f65316564613364352d356563382d343730382d386532352d3161303463356531316634382e706e67"><img src="https://camo.githubusercontent.com/65aa9be0a5faea5fb50ec17101ce3e900688ad9079ac67fed8bb7fb40c8578f1/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f65316564613364352d356563382d343730382d386532352d3161303463356531316634382e706e67" alt="img"></a></p>
<h3 id="3-死锁恢复">3. 死锁恢复</h3>
<ul>
<li>利用抢占恢复</li>
<li>利用回滚恢复</li>
<li>通过杀死进程恢复</li>
</ul>
<h2 id="死锁预防">死锁预防</h2>
<p>在程序运行之前预防发生死锁。</p>
<h3 id="1-破坏互斥条件">1. 破坏互斥条件</h3>
<p>例如假脱机打印机技术允许若干个进程同时输出，唯一真正请求物理打印机的进程是打印机守护进程。</p>
<h3 id="2-破坏占有和等待条件">2. 破坏占有和等待条件</h3>
<p>一种实现方式是规定所有进程在开始执行前请求所需要的全部资源。</p>
<h3 id="3-破坏不可抢占条件">3. 破坏不可抢占条件</h3>
<h3 id="4-破坏环路等待">4. 破坏环路等待</h3>
<p>给资源统一编号，进程只能按编号顺序来请求资源。</p>
<h2 id="死锁避免">死锁避免</h2>
<p>在程序运行时避免发生死锁。</p>
<h3 id="1-安全状态">1. 安全状态</h3>
<p><a href="https://camo.githubusercontent.com/09589c4bfe0e5514a44fc74cd63069f018b03246880e1f89b887313bbbdf2073/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f65643532333035312d363038662d346333662d623334332d3338336532643139343437302e706e67"><img src="https://camo.githubusercontent.com/09589c4bfe0e5514a44fc74cd63069f018b03246880e1f89b887313bbbdf2073/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f65643532333035312d363038662d346333662d623334332d3338336532643139343437302e706e67" alt="img"></a></p>
<p>图 a 的第二列 Has 表示已拥有的资源数，第三列 Max 表示总共需要的资源数，Free 表示还有可以使用的资源数。从图 a 开始出发，先让 B 拥有所需的所有资源（图 b），运行结束后释放 B，此时 Free 变为 5（图 c）；接着以同样的方式运行 C 和 A，使得所有进程都能成功运行，因此可以称图 a 所示的状态时安全的。</p>
<p>定义：如果没有死锁发生，并且即使所有进程突然请求对资源的最大需求，也仍然存在某种调度次序能够使得每一个进程运行完毕，则称该状态是安全的。</p>
<p>安全状态的检测与死锁的检测类似，因为安全状态必须要求不能发生死锁。下面的银行家算法与死锁检测算法非常类似，可以结合着做参考对比。</p>
<h3 id="2-单个资源的银行家算法">2. 单个资源的银行家算法</h3>
<p>一个小城镇的银行家，他向一群客户分别承诺了一定的贷款额度，算法要做的是判断对请求的满足是否会进入不安全状态，如果是，就拒绝请求；否则予以分配。</p>
<p><a href="https://camo.githubusercontent.com/381663ffc7a4db80478e5f454a11400debf6c13628632e3396fdaa7a6e555716/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f64313630656332652d636665322d343634302d626461372d3632663533653538623863302e706e67"><img src="https://camo.githubusercontent.com/381663ffc7a4db80478e5f454a11400debf6c13628632e3396fdaa7a6e555716/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f64313630656332652d636665322d343634302d626461372d3632663533653538623863302e706e67" alt="img"></a></p>
<p>上图 c 为不安全状态，因此算法会拒绝之前的请求，从而避免进入图 c 中的状态。</p>
<h3 id="3-多个资源的银行家算法">3. 多个资源的银行家算法</h3>
<p><a href="https://camo.githubusercontent.com/5334ac030b7446b89615c4319275fe27330481ac6f1a9280fc682f6dffcf5241/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f36326530646434662d343463332d343365652d626236652d6665646239653036383531392e706e67"><img src="https://camo.githubusercontent.com/5334ac030b7446b89615c4319275fe27330481ac6f1a9280fc682f6dffcf5241/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f36326530646434662d343463332d343365652d626236652d6665646239653036383531392e706e67" alt="img"></a></p>
<p>上图中有五个进程，四个资源。左边的图表示已经分配的资源，右边的图表示还需要分配的资源。最右边的 E、P 以及 A 分别表示：总资源、已分配资源以及可用资源，注意这三个为向量，而不是具体数值，例如 A=(1020)，表示 4 个资源分别还剩下 1/0/2/0。</p>
<p>检查一个状态是否安全的算法如下：</p>
<ul>
<li>查找右边的矩阵是否存在一行小于等于向量 A。如果不存在这样的行，那么系统将会发生死锁，状态是不安全的。</li>
<li>假若找到这样一行，将该进程标记为终止，并将其已分配资源加到 A 中。</li>
<li>重复以上两步，直到所有进程都标记为终止，则状态时安全的。</li>
</ul>
<p>如果一个状态不是安全的，需要拒绝进入这个状态。</p>
<h1 id="内存管理">内存管理</h1>
<h2 id="虚拟内存">虚拟内存</h2>
<p>虚拟内存的目的是为了让物理内存扩充成更大的逻辑内存，从而让程序获得更多的可用内存。</p>
<p>为了更好的管理内存，操作系统将内存抽象成地址空间。每个程序拥有自己的地址空间，这个地址空间被分割成多个块，每一块称为一页。这些页被映射到物理内存，但不需要映射到连续的物理内存，也不需要所有页都必须在物理内存中。当程序引用到不在物理内存中的页时，由硬件执行必要的映射，将缺失的部分装入物理内存并重新执行失败的指令。</p>
<p>从上面的描述中可以看出，虚拟内存允许程序不用将地址空间中的每一页都映射到物理内存，也就是说一个程序不需要全部调入内存就可以运行，这使得有限的内存运行大程序成为可能。例如有一台计算机可以产生 16 位地址，那么一个程序的地址空间范围是 0~64K。该计算机只有 32KB 的物理内存，虚拟内存技术允许该计算机运行一个 64K 大小的程序。</p>
<p><a href="https://camo.githubusercontent.com/01251b0ef66ccf744889c26424634aae680922be7d993522b4d831dca3c9511c/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f37623238316231652d303539352d343032622d616533352d3863393130383463333363312e706e67"><img src="https://camo.githubusercontent.com/01251b0ef66ccf744889c26424634aae680922be7d993522b4d831dca3c9511c/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f37623238316231652d303539352d343032622d616533352d3863393130383463333363312e706e67" alt="img"></a></p>
<h2 id="分页系统地址映射">分页系统地址映射</h2>
<p>内存管理单元（MMU）管理着地址空间和物理内存的转换，其中的页表（Page table）存储着页（程序地址空间）和页框（物理内存空间）的映射表。</p>
<p>一个虚拟地址分成两个部分，一部分存储页面号，一部分存储偏移量。</p>
<p>下图的页表存放着 16 个页，这 16 个页需要用 4 个比特位来进行索引定位。例如对于虚拟地址（0010 000000000100），前 4 位是存储页面号 2，读取表项内容为（110 1），页表项最后一位表示是否存在于内存中，1 表示存在。后 12 位存储偏移量。这个页对应的页框的地址为 （110 000000000100）。</p>
<p><a href="https://camo.githubusercontent.com/1f3a60c6aaac33dd000b9d6a39069d3ddaf2bb04c22b8bcda782eca707eb64fe/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f63663433383661312d353863392d346563612d613137662d6531326231653937373065622e706e67"><img src="https://camo.githubusercontent.com/1f3a60c6aaac33dd000b9d6a39069d3ddaf2bb04c22b8bcda782eca707eb64fe/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f63663433383661312d353863392d346563612d613137662d6531326231653937373065622e706e67" alt="img"></a></p>
<h2 id="页面置换算法">页面置换算法</h2>
<p>在程序运行过程中，如果要访问的页面不在内存中，就发生缺页中断从而将该页调入内存中。此时如果内存已无空闲空间，系统必须从内存中调出一个页面到磁盘对换区中来腾出空间。</p>
<p>页面置换算法和缓存淘汰策略类似，可以将内存看成磁盘的缓存。在缓存系统中，缓存的大小有限，当有新的缓存到达时，需要淘汰一部分已经存在的缓存，这样才有空间存放新的缓存数据。</p>
<p>页面置换算法的主要目标是使页面置换频率最低（也可以说缺页率最低）。</p>
<h3 id="1-最佳">1. 最佳</h3>
<blockquote>
<p>OPT, Optimal replacement algorithm</p>
</blockquote>
<p>所选择的被换出的页面将是最长时间内不再被访问，通常可以保证获得最低的缺页率。</p>
<p>是一种理论上的算法，因为无法知道一个页面多长时间不再被访问。</p>
<p>举例：一个系统为某进程分配了三个物理块，并有如下页面引用序列：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-fallback" data-lang="fallback">7，0，1，2，0，3，0，4，2，3，0，3，2，1，2，0，1，7，0，1
</code></pre></td></tr></table>
</div>
</div><p>开始运行时，先将 7, 0, 1 三个页面装入内存。当进程要访问页面 2 时，产生缺页中断，会将页面 7 换出，因为页面 7 再次被访问的时间最长。</p>
<h3 id="2-最近最久未使用">2. 最近最久未使用</h3>
<blockquote>
<p>LRU, Least Recently Used</p>
</blockquote>
<p>虽然无法知道将来要使用的页面情况，但是可以知道过去使用页面的情况。LRU 将最近最久未使用的页面换出。</p>
<p>为了实现 LRU，需要在内存中维护一个所有页面的链表。当一个页面被访问时，将这个页面移到链表表头。这样就能保证链表表尾的页面是最近最久未访问的。</p>
<p>因为每次访问都需要更新链表，因此这种方式实现的 LRU 代价很高。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-fallback" data-lang="fallback">4，7，0，7，1，0，1，2，1，2，6
</code></pre></td></tr></table>
</div>
</div><p><a href="https://camo.githubusercontent.com/c5cd2c10ae1c8526540a7af00c5390d1a953f147c4c147358953c9e929897cc3/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f65623835393232382d633066322d346263652d393130642d6439663736393239333532622e706e67"><img src="https://camo.githubusercontent.com/c5cd2c10ae1c8526540a7af00c5390d1a953f147c4c147358953c9e929897cc3/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f65623835393232382d633066322d346263652d393130642d6439663736393239333532622e706e67" alt="img"></a></p>
<p>### 3. 最近未使用</p>
<blockquote>
<p>NRU, Not Recently Used</p>
</blockquote>
<p>每个页面都有两个状态位：R 与 M，当页面被访问时设置页面的 R=1，当页面被修改时设置 M=1。其中 R 位会定时被清零。可以将页面分成以下四类：</p>
<ul>
<li>R=0，M=0</li>
<li>R=0，M=1</li>
<li>R=1，M=0</li>
<li>R=1，M=1</li>
</ul>
<p>当发生缺页中断时，NRU 算法随机地从类编号最小的非空类中挑选一个页面将它换出。</p>
<p>NRU 优先换出已经被修改的脏页面（R=0，M=1），而不是被频繁使用的干净页面（R=1，M=0）。</p>
<h3 id="4-先进先出">4. 先进先出</h3>
<blockquote>
<p>FIFO, First In First Out</p>
</blockquote>
<p>选择换出的页面是最先进入的页面。</p>
<p>该算法会将那些经常被访问的页面换出，导致缺页率升高。</p>
<h3 id="5-第二次机会算法">5. 第二次机会算法</h3>
<p>FIFO 算法可能会把经常使用的页面置换出去，为了避免这一问题，对该算法做一个简单的修改：</p>
<p>当页面被访问 (读或写) 时设置该页面的 R 位为 1。需要替换的时候，检查最老页面的 R 位。如果 R 位是 0，那么这个页面既老又没有被使用，可以立刻置换掉；如果是 1，就将 R 位清 0，并把该页面放到链表的尾端，修改它的装入时间使它就像刚装入的一样，然后继续从链表的头部开始搜索。</p>
<p><a href="https://camo.githubusercontent.com/579e409ef1551a1dc1c59487bc2fd54e93129ec97573721a3027376aa7f17595/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f65636638616435642d353430332d343862392d623665372d6632653230666665386663612e706e67"><img src="https://camo.githubusercontent.com/579e409ef1551a1dc1c59487bc2fd54e93129ec97573721a3027376aa7f17595/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f65636638616435642d353430332d343862392d623665372d6632653230666665386663612e706e67" alt="img"></a></p>
<h3 id="6-时钟">6. 时钟</h3>
<blockquote>
<p>Clock</p>
</blockquote>
<p>第二次机会算法需要在链表中移动页面，降低了效率。时钟算法使用环形链表将页面连接起来，再使用一个指针指向最老的页面。</p>
<p><a href="https://camo.githubusercontent.com/66bf1e33e909443e7fd77bf1d37c6144162b0545ac0aa6b078928e40a4d64878/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f35663565663062362d393865612d343937632d613030372d6636633535323838656162312e706e67"><img src="https://camo.githubusercontent.com/66bf1e33e909443e7fd77bf1d37c6144162b0545ac0aa6b078928e40a4d64878/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f35663565663062362d393865612d343937632d613030372d6636633535323838656162312e706e67" alt="img"></a></p>
<h2 id="分段">分段</h2>
<p>虚拟内存采用的是分页技术，也就是将地址空间划分成固定大小的页，每一页再与内存进行映射。</p>
<p>下图为一个编译器在编译过程中建立的多个表，有 4 个表是动态增长的，如果使用分页系统的一维地址空间，动态增长的特点会导致覆盖问题的出现。</p>
<p><a href="https://camo.githubusercontent.com/bc968c738c37aa7ad6d63d9b95a4803f11fe14aac87f571558024d19af30d399/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f32326465303533382d376336652d343336352d626433622d3863653363353930303231362e706e67"><img src="https://camo.githubusercontent.com/bc968c738c37aa7ad6d63d9b95a4803f11fe14aac87f571558024d19af30d399/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f32326465303533382d376336652d343336352d626433622d3863653363353930303231362e706e67" alt="img"></a></p>
<p>分段的做法是把每个表分成段，一个段构成一个独立的地址空间。每个段的长度可以不同，并且可以动态增长。</p>
<p><a href="https://camo.githubusercontent.com/836f0a92a1f8ff0dee7112c8fc213daa419a42e82cd543d6fd87100fc2624bec/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f65303930306262322d323230612d343362372d396161392d3164356364353566663536652e706e67"><img src="https://camo.githubusercontent.com/836f0a92a1f8ff0dee7112c8fc213daa419a42e82cd543d6fd87100fc2624bec/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f65303930306262322d323230612d343362372d396161392d3164356364353566663536652e706e67" alt="img"></a></p>
<h2 id="段页式">段页式</h2>
<p>程序的地址空间划分成多个拥有独立地址空间的段，每个段上的地址空间划分成大小相同的页。这样既拥有分段系统的共享和保护，又拥有分页系统的虚拟内存功能。</p>
<h2 id="分页与分段的比较">分页与分段的比较</h2>
<ul>
<li>对程序员的透明性：分页透明，但是分段需要程序员显式划分每个段。</li>
<li>地址空间的维度：分页是一维地址空间，分段是二维的。</li>
<li>大小是否可以改变：页的大小不可变，段的大小可以动态改变。</li>
<li>出现的原因：分页主要用于实现虚拟内存，从而获得更大的地址空间；分段主要是为了使程序和数据可以被划分为逻辑上独立的地址空间并且有助于共享和保护。</li>
</ul>
<h1 id="链接">链接</h1>
<h2 id="编译系统">编译系统</h2>
<ul>
<li>预处理阶段：处理以 # 开头的预处理命令；</li>
<li>编译阶段：翻译成汇编文件；</li>
<li>汇编阶段：将汇编文件翻译成可重定位目标文件；</li>
<li>链接阶段：将可重定位目标文件和 printf.o 等单独预编译好的目标文件进行合并，得到最终的可执行目标文件。</li>
</ul>
<h2 id="静态链接">静态链接</h2>
<p>静态链接器以一组可重定位目标文件为输入，生成一个完全链接的可执行目标文件作为输出。链接器主要完成以下两个任务：</p>
<ul>
<li>符号解析：每个符号对应于一个函数、一个全局变量或一个静态变量，符号解析的目的是将每个符号引用与一个符号定义关联起来。</li>
<li>重定位：链接器通过把每个符号定义与一个内存位置关联起来，然后修改所有对这些符号的引用，使得它们指向这个内存位置。</li>
</ul>
<h2 id="目标文件">目标文件</h2>
<ul>
<li>可执行目标文件：可以直接在内存中执行；</li>
<li>可重定位目标文件：可与其它可重定位目标文件在链接阶段合并，创建一个可执行目标文件；</li>
<li>共享目标文件：这是一种特殊的可重定位目标文件，可以在运行时被动态加载进内存并链接；</li>
</ul>
<h2 id="动态链接">动态链接</h2>
<p>静态库有以下两个问题：</p>
<ul>
<li>当静态库更新时那么整个程序都要重新进行链接；</li>
<li>对于 printf 这种标准函数库，如果每个程序都要有代码，这会极大浪费资源。</li>
</ul>
<p>共享库是为了解决静态库的这两个问题而设计的，在 Linux 系统中通常用 .so 后缀来表示，Windows 系统上它们被称为 DLL。它具有以下特点：</p>
<ul>
<li>在给定的文件系统中一个库只有一个文件，所有引用该库的可执行目标文件都共享这个文件，它不会被复制到引用它的可执行文件中；</li>
<li>在内存中，一个共享库的 .text 节（已编译程序的机器代码）的一个副本可以被不同的正在运行的进程共享。</li>
</ul>
<p><a href="https://camo.githubusercontent.com/e4bd3c0074bc832f7b8f4d7dda678b26a51185668d5afc2f14af14198051d1eb/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f37366463373736392d316161632d343838382d396265612d3036346631636161386537372e6a7067"><img src="https://camo.githubusercontent.com/e4bd3c0074bc832f7b8f4d7dda678b26a51185668d5afc2f14af14198051d1eb/68747470733a2f2f63732d6e6f7465732d313235363130393739362e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f37366463373736392d316161632d343838382d396265612d3036346631636161386537372e6a7067" alt="img"></a></p>

    </div>

    <div class="post-copyright">
  <p class="copyright-item">
    <span class="item-title">Author</span>
    <span class="item-content">olOwOlo</span>
  </p>
  <p class="copyright-item">
    <span class="item-title">LastMod</span>
    <span class="item-content">
        2021-06-01
        
    </span>
  </p>
  
  
</div>
<footer class="post-footer">
      <div class="post-tags">
          <a href="/tags/%E5%A4%A7%E5%AD%A6%E8%AF%BE%E7%A8%8B/">大学课程</a>
          </div>
      <nav class="post-nav">
        <a class="prev" href="/post/%E9%9C%80%E6%B1%82%E5%B7%A5%E7%A8%8B/">
            <i class="iconfont icon-left"></i>
            <span class="prev-text nav-default">需求工程</span>
            <span class="prev-text nav-mobile">Prev</span>
          </a>
        <a class="next" href="/post/%E9%98%BF%E9%87%8C%E9%9D%A2%E8%AF%95%E7%BB%8F%E5%8E%86/">
            <span class="next-text nav-default">阿里面试经历</span>
            <span class="next-text nav-mobile">Next</span>
            <i class="iconfont icon-right"></i>
          </a>
      </nav>
    </footer>
  </article>
        </div>
        

  

  

      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="social-links">
      <a href="mailto:your@email.com" class="iconfont icon-email" title="email"></a>
      <a href="http://localhost:1313" class="iconfont icon-stack-overflow" title="stack-overflow"></a>
      <a href="http://localhost:1313" class="iconfont icon-twitter" title="twitter"></a>
      <a href="http://localhost:1313" class="iconfont icon-facebook" title="facebook"></a>
      <a href="http://localhost:1313" class="iconfont icon-linkedin" title="linkedin"></a>
      <a href="http://localhost:1313" class="iconfont icon-google" title="google"></a>
      <a href="http://localhost:1313" class="iconfont icon-github" title="github"></a>
      <a href="http://localhost:1313" class="iconfont icon-weibo" title="weibo"></a>
      <a href="http://localhost:1313" class="iconfont icon-zhihu" title="zhihu"></a>
      <a href="http://localhost:1313" class="iconfont icon-douban" title="douban"></a>
      <a href="http://localhost:1313" class="iconfont icon-pocket" title="pocket"></a>
      <a href="http://localhost:1313" class="iconfont icon-tumblr" title="tumblr"></a>
      <a href="http://localhost:1313" class="iconfont icon-instagram" title="instagram"></a>
      <a href="http://localhost:1313" class="iconfont icon-gitlab" title="gitlab"></a>
      <a href="http://localhost:1313" class="iconfont icon-bilibili" title="bilibili"></a>
  <a href="https://xiongshou.github.io/index.xml" type="application/rss+xml" class="iconfont icon-rss" title="rss"></a>
</div>

<div class="copyright">
  <span class="power-by">
    Powered by <a class="hexo-link" href="https://gohugo.io">Hugo</a>
  </span>
  <span class="division">|</span>
  <span class="theme-info">
    Theme - 
    <a class="theme-link" href="https://github.com/olOwOlo/hugo-theme-even">Even</a>
  </span>

  

  <span class="copyright-year">
    &copy; 
    2017 - 
    2021<span class="heart"><i class="iconfont icon-heart"></i></span><span>olOwOlo</span>
  </span>
</div>

    </footer>

    <div class="back-to-top" id="back-to-top">
      <i class="iconfont icon-up"></i>
    </div>
  </div>
  
  <script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/slideout@1.0.1/dist/slideout.min.js" integrity="sha256-t+zJ/g8/KXIJMjSVQdnibt4dlaDxc9zXr/9oNPeWqdg=" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.1.20/dist/jquery.fancybox.min.js" integrity="sha256-XVLffZaxoWfGUEbdzuLi7pwaUJv1cecsQJQqGLe7axY=" crossorigin="anonymous"></script>



<script type="text/javascript" src="/js/main.min.c99b103c33d1539acf3025e1913697534542c4a5aa5af0ccc20475ed2863603b.js"></script>








</body>
</html>
